import { CMEVENTS, DocumentObject, FeatureManager } from '@amcax/base';
import { docMgr, userEvents, dataManager } from '@app-base/module';
import { wsApi } from '@app-base/server';
import _ from 'lodash';
import { Vector3Tuple } from 'three';
import { getPointPosition } from '../cmd-elec-box';
import { ELEC_EVENT } from '@app-base/common/Constants';

const PLANE_ID = 'planeId';
export class AntennaInfo {
  //  飞机id
  planeId: string = '';
  antennaMap: Map<string, AntennaType>;
  static instance: AntennaInfo;
  private resetGetAntenna: () => void;
  private handlersNum = 0;
  private idAndLabelNameMap: Map<string, string>;
  static getInstance(): AntennaInfo {
    if (!this.instance) {
      this.instance = new AntennaInfo();
    }
    return this.instance;
  }
  constructor() {
    this.antennaMap = new Map();
    this.init();
    this.planeId = localStorage.getItem(PLANE_ID) || '';
    this.resetGetAntenna = _.throttle(() => this.init(), 2000, {
      trailing: false,
    });
    userEvents.on(CMEVENTS.MODEL_ADDED, this.modelChange);
    userEvents.on(ELEC_EVENT.ELEC_MODEL_ADDED, this.modelAdded);
  }
  set PlaneId(planeId: string) {
    localStorage.setItem(PLANE_ID, planeId);
    this.planeId = planeId;
  }
  modelChange = () => {
    this.handlersNum--;
  };
  setHandlersNum() {
    this.handlersNum++;
  }
  getHandlersNum() {
    return this.handlersNum;
  }
  setIdAndLabelNameMap(dxid: string, label: string) {
    this.idAndLabelNameMap.set(dxid, label);
  }
  modelAdded = async () => {
    // 确保名称正确
    this.idAndLabelNameMap?.forEach(async (label, dxid) => {
      try {
        await wsApi.setLabel(dxid, label);
        this.idAndLabelNameMap.delete(dxid);
      } catch (e) {
        console.log(e);
      }
    });
    // if (!this.idAndLabelNameMap || this.idAndLabelNameMap?.size <= 0) {
    setTimeout(async () => {
      await docMgr.updateDocumentObjects();
      docMgr.resetDocumentObject();
    }, 1000);
    // }
  };
  init() {
    this.antennaMap.clear();
    this.getGeomBox()
      .filter((item) => !item.label.startsWith('零件几何体.1'))
      .forEach((doc) => {
        const { theta, phi } = this.getRotation(doc?.children);
        const translateParam = this.getTranslation(doc?.children);
        const end = translateParam?.end || [0, 0, 0];
        const start = translateParam?.start || [0, 0, 0];
        const origin = translateParam?.origin || [0, 0, 0];
        this.antennaMap.set(doc.dxid, {
          dxid: doc.dxid,
          name: doc.label,
          args: {
            x: end[0] || 0,
            y: end[1] || 0,
            z: end[2] || 0,
            sx: start[0] || 0,
            sy: start[1] || 0,
            sz: start[2] || 0,
            ox: origin[0] || 0,
            oy: origin[1] || 0,
            oz: origin[2] || 0,
            theta,
            phi,
            dxid: translateParam.dxid,
          },
        });
      });
    if (FeatureManager.isFeatureEnabled('set-current-box')) {
      docMgr.workingObjectDxid = docMgr.documentObjects
        ?.filter((item) => item.label == '零件几何体.1')
        ?.at(0)?.dxid;
      wsApi.execCmd({
        c: 'SetCurrentBox',
        o: docMgr.workingObjectDxid,
      });
    }
  }
  private getTranslation(child: DocumentObject[]) {
    return this.getSpecifyType(child, 'Part::Translation');
  }
  private getRotation(child: DocumentObject[]) {
    const rotationParam = this.getSpecifyType(child, 'Part::Rotate');
    return {
      phi: rotationParam.angle2 || 0,
      theta: rotationParam.angle || 0,
    };
  }
  private getFreePoints(child: DocumentObject[]) {
    return this.getSpecifyTypeAll(child, 'Part::Vertex');
  }
  private getElecBox(child: DocumentObject[]) {
    return this.getSpecifyTypeAll(child, 'Part::Box');
  }
  private getSpecifyTypeAll(child: DocumentObject[], type: string) {
    const info = child.filter((item) => item?.type === type);
    if (!info) {
      return [{ dxid: child?.[0].dxid }];
    }
    return info || [];
  }
  private getSpecifyType(child: DocumentObject[], type: string) {
    const info = child.findLast((item) => item?.type === type);
    if (!info) {
      return { dxid: child?.[0].dxid };
    }
    info?.params && (info.params.dxid = info.dxid);
    return info.params || {};
  }
  // 获取父级dxid value -> dxid or label
  private getParentDxid(value: string) {
    return docMgr.documentObjects.find((item) => {
      return (
        item.dxid === value ||
        item.label === value ||
        (item.type === 'GeomBox' &&
          item.children?.some((c) => value === c.dxid || value === c.label))
      );
    });
  }
  private getGeomBox() {
    return docMgr.documentObjects.filter(
      (item) =>
        item.type === 'GeomBox' &&
        item?.children?.length > 0 &&
        this.planeId !== item.dxid,
    );
  }
  // 根据 zdir 过滤点
  private filterPoints(points, keyPoint, zdir) {
    const filteredPoints = [];
    const pointSet = new Set();
    for (const point of points) {
      let key;
      if (zdir === 'YOZ') {
        if (point[0] != keyPoint[0]) continue;
        key = `${point[1]},${point[2]}`;
      } else if (zdir === 'XOZ') {
        if (point[1] != keyPoint[1]) continue;
        key = `${point[0]},${point[2]}`;
      } else if (zdir === 'XOY') {
        if (point[2] != keyPoint[2]) continue;
        key = `${point[0]},${point[1]}`;
      }
      if (!pointSet.has(key)) {
        pointSet.add(key);
        filteredPoints.push(point);
      }
    }
    return filteredPoints;
  }
  // 根据过滤后的点过滤边
  private filterEdges(edges, filteredPoints) {
    const filteredEdges = [];
    const pointMap = new Map();
    for (const [index, point] of filteredPoints.entries()) {
      pointMap.set(JSON.stringify(point), index);
    }
    for (const edge of edges) {
      const [p1, p2] = edge.mesh;
      if (
        pointMap.has(JSON.stringify(p1)) &&
        pointMap.has(JSON.stringify(p2))
      ) {
        filteredEdges.push(edge);
      }
    }
    return filteredEdges;
  }
  private getFreePoint() {
    let result = [];
    const names = JSON.parse(localStorage.getItem('elecWireNames') || '[]');
    const labels = ['零件几何体.1', ...names];
    labels.forEach((label) => {
      const parent = docMgr.documentObjects.filter(
        (item) =>
          item.type === 'GeomBox' &&
          item.label.startsWith(
            label + (label === '零件几何体.1' ? '' : '点设置'),
          ),
      );
      if (parent && parent.length > 0) {
        parent.forEach((doc) => {
          const freePoints = this.getFreePoints(doc?.children);
          const res = freePoints?.map((item) => ({
            id: item.dxid,
            name: item.label.replace(label + '-', ''),
            from: label,
            point: item.params.points[0],
          }));
          if (res && res.length > 0) {
            result = [...result, ...res];
          }
        });
      }
    });
    return result;
  }
  public getAntennaInfoById(dxid: string) {
    this.resetGetAntenna();
    const doc = this.getParentDxid(dxid);
    const id = doc?.dxid || dxid;
    return this.antennaMap.get(id) || { args };
  }
  public exportPointJson() {
    this.resetGetAntenna();
    const temp = [...this.antennaMap.values()];
    const json =
      temp.map((item) => {
        return {
          name: item.name,
          args: {
            x: item.args.x,
            y: item.args.y,
            z: item.args.z,
            sx: item.args.ox,
            sy: item.args.oy,
            sz: item.args.oz,
            theta: item.args.theta,
            phi: item.args.phi,
          },
        };
      }) || [];
    return JSON.stringify(json);
  }
  public exportFreePointJson() {
    this.resetGetAntenna();
    let result = this.getFreePoint();
    if (!result || result.length < 0) {
      this.getGeomBox().forEach((doc) => {
        const freePoints = this.getFreePoints(doc?.children);
        const res = freePoints?.map((item) => ({
          id: item.dxid,
          name: item.label,
          from: '',
          point: item.params.points[0],
        }));
        if (res && res.length > 0) {
          result = [...result, ...res];
        }
      });
    }
    return JSON.stringify(result);
  }
  public exportElecBoxJson() {
    let result = [];
    this.resetGetAntenna();
    this.getGeomBox().forEach((doc) => {
      const getElecBox = this.getElecBox(doc?.children);

      const res = getElecBox?.map((item) => {
        const shape = dataManager.getShapeData(item.shape);
        const origin = getPointPosition(item.params.origin);
        let points = shape?.vertices?.topo ?? [];
        let edges = shape?.edges ?? [];
        if (item.params.boxMode === 'flat') {
          points = this.filterPoints(points, origin, item.params.zdir);
          edges = this.filterEdges(edges, points);
        }
        return {
          params: {
            ...item.params,
            origin: origin,
          },
          points: points,
          edges: edges,
          result: {
            zdir: item.params.zdir.replace('O', ''),
            x: Math.min(...points.map((point) => point[0])),
            y: Math.min(...points.map((point) => point[1])),
            z: Math.min(...points.map((point) => point[2])),
            x1: Math.max(...points.map((point) => point[0])),
            y1: Math.max(...points.map((point) => point[1])),
            z1: Math.max(...points.map((point) => point[2])),
            nx: item.params.nx,
            ny: item.params.ny,
            nz: item.params.nz,
          },
        };
      });
      if (res && res.length > 0) {
        result = [...result, ...res];
      }
    });
    return JSON.stringify(result);
  }
  // 同一个模型下，如果存在多个visibility 为 true，则只取最后一个 其他的都改为false
  setVisable() {
    this.getGeomBox().forEach((item) => {
      const arr = item.children.map((item) => {
        return {
          id: item.dxid,
          visible: item.visibility,
        };
      });
      arr.pop();
      arr.forEach((item) => {
        item.visible && wsApi.setVisible(item.id, false);
      });
    });
  }
  // 合并同一几何体体下的模型
  async compound(ids) {
    for (const item of this.getGeomBox()) {
      if (ids.includes(item.dxid) && item.children?.length > 1) {
        await wsApi.createCompound(item.children.map((item) => item.dxid));
      }
    }
  }
}
export const antennaInfo = () => {
  return AntennaInfo.getInstance();
};

export const args = {
  x: 0,
  y: 0,
  z: 0,
  theta: 0,
  phi: 0,
  dxid: '',
};

export type AntennaType = {
  dxid?: string;
  cadModel?: string;
  url?: string;
  name: string;
  args: argsType;
  type?: 'antenna' | 'plane';
};
type argsType = {
  x?: number;
  y?: number;
  z?: number;
  sx?: number;
  sy?: number;
  sz?: number;
  ox?: number;
  oy?: number;
  oz?: number;
  theta?: number;
  phi?: number;
  dxid?: string;
  rotateDxid?: string;
  points?: Vector3Tuple;
};
